package org.atlantis.uzw.base.event;

import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;

import org.atlantis.uzw.utils.DaemonSharedExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ThreadEventRouter implements EventRouter {

	private static final Logger log = LoggerFactory.getLogger(ThreadEventRouter.class);

	private ConcurrentMap<String, List<EventInvoker>> invokerRegister = new ConcurrentHashMap<String, List<EventInvoker>>() {
		private static final long serialVersionUID = -2870670013025317133L;

		@Override
		public List<EventInvoker> get(Object key) {
			List<EventInvoker> invokers = super.get(key);
			if (invokers != null) return invokers;
			synchronized (this) {
				invokers = super.get(key);
				if (invokers != null) return invokers;
				invokers = new CopyOnWriteArrayList<>();
				this.put((String) key, invokers);
				return invokers;
			}
		}
	};

	@Override
	public void publish(String topic, final Object event) {
		List<EventInvoker> invokers = invokerRegister.get(topic);
		if (invokers.isEmpty()) return;
		for (final EventInvoker invoker : invokers) {
			DaemonSharedExecutor.executor.execute(new Runnable() {
				@Override
				public void run() {
					invoker.invoke(event);
				}
			});
		}
	}

	void register(String topic, Object instance, Method method) {
		invokerRegister.get(topic).add(new EventInvoker(instance, method));
		log.info("register event handler: {} to topic: {} successfully", method.toString(), topic);
	}

	private static class EventInvoker {
		private final Object instance;
		private final Method method;

		public EventInvoker(Object instance, Method method) {
			this.instance = instance;
			this.method = method;
		}

		public void invoke(Object event) {
			try {
				method.invoke(instance, event);
			} catch (Exception e) {
				log.error("Could not invoke event handler method: " + method, e);
			}
		}
	}
}
